/* compile.c */

//TODO 初步成形

#include <windows.h>

#include "trie.c"

#define BAD_HANDLE(x) (x == INVALID_HANDLE_VALUE || x == NULL)

#define TYPE_INVALID 0
#define TYPE_SIGN 1
#define TYPE_WORD 2
#define TYPE_RESULT 3

struct Item {
    DWORD dwType;
    LPSTR lpszWord;
};

struct Trie g_Vars;

struct Item g_ItemStack[0x40];
DWORD g_dwStackPtr = 0;

BOOL GetLine(HANDLE hIn, LPSTR lpszBuffer, DWORD dwSize){
    DWORD dwRead;
    DWORD dwIndex;

    ReadFile(hIn, lpszBuffer, dwSize, &dwRead, NULL);
    for(dwIndex = 0; dwIndex < dwRead; dwIndex++){
        if(lpszBuffer[dwIndex] == '\r'){
            lpszBuffer[dwIndex] = '\0';
            if(dwIndex + 1 < dwRead && lpszBuffer[dwIndex + 1] == '\n'){
                SetFilePointer(hIn, dwIndex - dwRead + 2, NULL, FILE_CURRENT);
                return TRUE;
            }
            SetFilePointer(hIn, dwIndex - dwRead + 1, NULL, FILE_CURRENT);
            return TRUE;
        }
        if(lpszBuffer[dwIndex] == '\n'){
            lpszBuffer[dwIndex] = '\0';
            SetFilePointer(hIn, dwIndex - dwRead + 1, NULL, FILE_CURRENT);
            return TRUE;
        }
    }
    return FALSE;
}

BYTE CharType(CHAR c){
    static BYTE bList[0x100] = {0};

    if(c == -1){
        DWORD dwIndex;
        LPCSTR lpszSeq;

        lpszSeq = "-_0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ";
        dwIndex = 0;
        while(lpszSeq[dwIndex])
            bList[(BYTE)lpszSeq[dwIndex++]] = 1;
        lpszSeq = "+-*/%&^|!><=;,";
        dwIndex = 0;
        while(lpszSeq[dwIndex])
            bList[(BYTE)lpszSeq[dwIndex++]] = 2;
        lpszSeq = "\t\b\r\n ";
        dwIndex = 0;
        while(lpszSeq[dwIndex])
            bList[(BYTE)lpszSeq[dwIndex++]] = 3;
        return 0;
    }
    return bList[(BYTE)c];
}

BYTE SignPriority(CHAR c){
    static BYTE bList[0x100] = {
        ['='] = 1,
        ['&'] = 2,
        ['|'] = 2,
        ['!'] = 2,
        ['^'] = 2,
        ['>'] = 3,
        ['<'] = 3,
        ['+'] = 4,
        ['-'] = 4,
        ['*'] = 5,
        ['/'] = 5,
        ['%'] = 5,
    };

    return bList[(BYTE)c];
}

void LoadVariable(HANDLE hOut, struct Item* pVar){
    static CHAR cBuffer[0x40];
    DWORD dwLength;

    if(pVar->dwType == TYPE_WORD){
        if((*pVar->lpszWord >= '0' && *pVar->lpszWord <= '9') || *pVar->lpszWord == '-'){
            dwLength = wsprintfA(cBuffer, "push %s\n", pVar->lpszWord);
            WriteFile(hOut, cBuffer, dwLength, NULL, NULL);
        }else{
            dwLength = wsprintfA(cBuffer, "load %lu\n", *((LPDWORD)TrieQuery(&g_Vars, pVar->lpszWord)));
            WriteFile(hOut, cBuffer, dwLength, NULL, NULL);
        }
    }
}

void StoreVariable(HANDLE hOut, struct Item* pVar){
    static CHAR cBuffer[0x40];
    DWORD dwLength;

    dwLength = wsprintfA(cBuffer, "store %lu\n", *((LPDWORD)TrieQuery(&g_Vars, pVar->lpszWord)));
    WriteFile(hOut, cBuffer, dwLength, NULL, NULL);
}

void op_mov(HANDLE hOut){
    LoadVariable(hOut, g_ItemStack + (--g_dwStackPtr));
    StoreVariable(hOut, g_ItemStack + g_dwStackPtr - 1);
}

void op_and(HANDLE hOut){
    LoadVariable(hOut, g_ItemStack + (--g_dwStackPtr));
    LoadVariable(hOut, g_ItemStack + g_dwStackPtr - 1);
    WriteFile(hOut, "and\n", 4, NULL, NULL);
    g_ItemStack[g_dwStackPtr - 1].dwType = TYPE_RESULT;
}

void op_or(HANDLE hOut){
    LoadVariable(hOut, g_ItemStack + (--g_dwStackPtr));
    LoadVariable(hOut, g_ItemStack + g_dwStackPtr - 1);
    WriteFile(hOut, "or\n", 3, NULL, NULL);
    g_ItemStack[g_dwStackPtr - 1].dwType = TYPE_RESULT;
}

void op_not(HANDLE hOut){
    LoadVariable(hOut, g_ItemStack + g_dwStackPtr - 1);
    WriteFile(hOut, "not\n", 4, NULL, NULL);
    g_ItemStack[g_dwStackPtr - 1].dwType = TYPE_RESULT;
}

void op_xor(HANDLE hOut){
    LoadVariable(hOut, g_ItemStack + (--g_dwStackPtr));
    LoadVariable(hOut, g_ItemStack + g_dwStackPtr - 1);
    WriteFile(hOut, "xor\n", 4, NULL, NULL);
    g_ItemStack[g_dwStackPtr - 1].dwType = TYPE_RESULT;
}

void op_cmpg(HANDLE hOut){
    LoadVariable(hOut, g_ItemStack + (--g_dwStackPtr));
    LoadVariable(hOut, g_ItemStack + g_dwStackPtr - 1);
    WriteFile(hOut, "cmpg\n", 5, NULL, NULL);
    g_ItemStack[g_dwStackPtr - 1].dwType = TYPE_RESULT;
}

void op_cmpl(HANDLE hOut){
    LoadVariable(hOut, g_ItemStack + (--g_dwStackPtr));
    LoadVariable(hOut, g_ItemStack + g_dwStackPtr - 1);
    WriteFile(hOut, "cmpl\n", 5, NULL, NULL);
    g_ItemStack[g_dwStackPtr - 1].dwType = TYPE_RESULT;
}

void op_add(HANDLE hOut){
    LoadVariable(hOut, g_ItemStack + (--g_dwStackPtr));
    LoadVariable(hOut, g_ItemStack + g_dwStackPtr - 1);
    WriteFile(hOut, "add\n", 4, NULL, NULL);
    g_ItemStack[g_dwStackPtr - 1].dwType = TYPE_RESULT;
}

void op_sub(HANDLE hOut){
    LoadVariable(hOut, g_ItemStack + (--g_dwStackPtr));
    LoadVariable(hOut, g_ItemStack + g_dwStackPtr - 1);
    WriteFile(hOut, "sub\n", 4, NULL, NULL);
    g_ItemStack[g_dwStackPtr - 1].dwType = TYPE_RESULT;
}

void op_mul(HANDLE hOut){
    LoadVariable(hOut, g_ItemStack + (--g_dwStackPtr));
    LoadVariable(hOut, g_ItemStack + g_dwStackPtr - 1);
    WriteFile(hOut, "mul\n", 4, NULL, NULL);
    g_ItemStack[g_dwStackPtr - 1].dwType = TYPE_RESULT;
}

void op_div(HANDLE hOut){
    LoadVariable(hOut, g_ItemStack + (--g_dwStackPtr));
    LoadVariable(hOut, g_ItemStack + g_dwStackPtr - 1);
    WriteFile(hOut, "div\n", 4, NULL, NULL);
    g_ItemStack[g_dwStackPtr - 1].dwType = TYPE_RESULT;
}

void op_rem(HANDLE hOut){
    LoadVariable(hOut, g_ItemStack + (--g_dwStackPtr));
    LoadVariable(hOut, g_ItemStack + g_dwStackPtr - 1);
    WriteFile(hOut, "rem\n", 4, NULL, NULL);
    g_ItemStack[g_dwStackPtr - 1].dwType = TYPE_RESULT;
}

void SignOperation(CHAR c, HANDLE hOut){
    static void(*pHandlers[0x100])(HANDLE) = {
        ['='] = op_mov,
        ['&'] = op_and,
        ['|'] = op_or,
        ['!'] = op_not,
        ['^'] = op_xor,
        ['>'] = op_cmpg,
        ['<'] = op_cmpl,
        ['+'] = op_add,
        ['-'] = op_sub,
        ['*'] = op_mul,
        ['/'] = op_div,
        ['%'] = op_rem,
    };

    if(pHandlers[(BYTE)c])
        pHandlers[(BYTE)c](hOut);
}

BYTE GetNextWord(LPCSTR lpszLine, LPSTR lpszWord, DWORD dwSize){
    static DWORD dwIndex;
    DWORD dwPos = 0;

    if(lpszLine == NULL){
        dwIndex = 0;
        return 0;
    }
    while(lpszLine[dwIndex]){
        switch (CharType(lpszLine[dwIndex])) {
        case 0:
            return TYPE_INVALID;

        case 1:
            if(dwPos < dwSize)
                lpszWord[dwPos++] = lpszLine[dwIndex];
            break;
        case 2:
            if(dwPos){
                if(dwPos < dwSize)
                    lpszWord[dwPos] = '\0';
                return TYPE_WORD;
            }
            if(dwSize)
                *lpszWord = lpszLine[dwIndex];
            if(dwSize > 1)
                lpszWord[1] = '\0';
            dwIndex++;
            return TYPE_SIGN;

        case 3:
            if(dwPos){
                if(dwPos < dwSize)
                    lpszWord[dwPos] = '\0';
                return TYPE_WORD;
            }
            break;
        }
        dwIndex++;
    }
    if(dwPos){
        if(dwPos < dwSize)
            lpszWord[dwPos] = '\0';
        return TYPE_WORD;
    }
    return 0;
}

void ParseExpression(struct Item* pItems, DWORD dwStart, DWORD dwEnd, HANDLE hOut){
    DWORD dwIndex;
    DWORD dwMin = 0xFFFFFFFF;
    DWORD dwPos = -1;

    //if(dwStart == dwEnd) error;
    if(dwStart + 1 == dwEnd){
        if(pItems[dwStart].dwType == TYPE_WORD){
            g_ItemStack[g_dwStackPtr++] = pItems[dwStart];
        }
        return;
    }
    for(dwIndex = dwStart; dwIndex < dwEnd; dwIndex++)
        if(pItems[dwIndex].dwType == TYPE_SIGN && SignPriority(*pItems[dwIndex].lpszWord) <= dwMin){
            dwMin = SignPriority(*pItems[dwIndex].lpszWord);
            dwPos = dwIndex;
        }
    if(dwPos == -1)
        return;
    ParseExpression(pItems, dwStart, dwPos, hOut);
    ParseExpression(pItems, dwPos + 1, dwEnd, hOut);
    SignOperation(*pItems[dwPos].lpszWord, hOut);
}

void Compile(HANDLE hIn, HANDLE hOut){
    static CHAR lpszLine[0x100];
    static CHAR lpszWord[0x40];
    static struct Item pItems[0x40];

    CharType(-1);
    while(GetLine(hIn, lpszLine, 0xFF)){
        static DWORD dwIndex = 0;
        DWORD dwType;

        GetNextWord(NULL, NULL, 0);
        while((dwType = GetNextWord(lpszLine, lpszWord, 0x3F))){
            if(dwType == TYPE_SIGN && !strcmp(lpszWord, ";")){
                DWORD dwPtr;

                if(!strcmp(pItems->lpszWord, "int")){
                    static DWORD dwId = 0;

                    free(pItems->lpszWord);
                    for(dwPtr = 1; dwPtr < dwIndex; dwPtr++){
                        if(pItems[dwPtr].dwType == TYPE_WORD)
                            *((LPDWORD)TrieInsert(&g_Vars, pItems[dwPtr].lpszWord)) = dwId++;
                        free(pItems[dwPtr].lpszWord);
                    }
                    dwIndex = 0;
                    continue;
                }
                ParseExpression(pItems, 0, dwIndex, hOut);
                g_dwStackPtr = 0;
                for(dwPtr = 0; dwPtr < dwIndex; dwPtr++)
                    free(pItems[dwPtr].lpszWord);
                dwIndex = 0;
                continue;
            }else if(dwIndex < 0x40){
                DWORD dwLength = strlen(lpszWord) + 1;

                pItems[dwIndex].dwType = dwType;
                pItems[dwIndex].lpszWord = malloc(dwLength);
                RtlCopyMemory(pItems[dwIndex++].lpszWord, lpszWord, dwLength);
            }
        }
    }
}

int main(int argc, char** argv){
    HANDLE hOut;
    HANDLE hFile;
    HANDLE hOutFile;

    hOut = GetStdHandle(STD_OUTPUT_HANDLE);
    if(argc != 3){
        WriteFile(hOut, "Usage: compile.exe [file] [out]\r\n", 33, NULL, NULL);
        ExitProcess(1);
    }
    hFile = CreateFileA(
        argv[1],
        GENERIC_READ,
        0,
        NULL,
        OPEN_EXISTING,
        FILE_ATTRIBUTE_NORMAL,
        NULL
    );
    if(BAD_HANDLE(hFile)){
        WriteFile(hOut, "fatal error: Cannot open input file.\r\n", 38, NULL, NULL);
        ExitProcess(1);
    }
    hOutFile = CreateFileA(
        argv[2],
        GENERIC_WRITE,
        0,
        NULL,
        CREATE_ALWAYS,
        FILE_ATTRIBUTE_NORMAL,
        NULL
    );
    if(BAD_HANDLE(hOutFile)){
        WriteFile(hOut, "fatal error: Cannot create output file.\r\n", 41, NULL, NULL);
        ExitProcess(1);
    }
    g_Vars = TrieCreate();
    Compile(hFile, hOutFile);
    CloseHandle(hFile);
    CloseHandle(hOutFile);
    TrieDelete(&g_Vars);
    ExitProcess(0);
}
